#include "TCPServer.h"
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <map>
#include "../packet_headers/iphdr.h"
using namespace std;


sockaddr_in TCPServer::host_to_addr(std::string _host, unsigned short _port) {
	struct sockaddr_in output;
	struct hostent * he;

	output.sin_family = AF_INET;
	output.sin_port = htons(_port);

	// Get host by addr
	output.sin_addr.s_addr = inet_addr(_host.c_str());
	//output.sin_addr.s_addr = INADDR_ANY;

	return output;
}



TCPConnection TCPServer::receive_incoming_connection() {
	struct sockaddr_in newaddr;
	int len = sizeof(struct sockaddr_in);
	int newsock = accept(incoming_sock,
			(struct sockaddr*)&newaddr,
			(socklen_t*)&len);
	if(newsock < 0) {
		std::string err_msg = "";
		if(errno == ECONNABORTED) {
			err_msg = "Connection aborted.";
		}
		else if(errno == EPERM) {
			err_msg = "Incoming connection refused by firewall.";
		}
		else {
			err_msg = "Could not receive incoming connection.";
		}
		printf("Error: %s\n", err_msg.c_str());
	}
	/* CREATE TCPConnection from new connection data */
	TCPConnection newtcpconn;
	newtcpconn.sock = newsock;
	newtcpconn.addr = newaddr;

	return newtcpconn;
}

void TCPServer::handle_server_connection(TCPServer server, TCPConnection conn, char* sock_message){
	int new_client_sockfd;
	server.maxfdp = conn.sock;
	server.maxclient = 4;	//no mutiple connection
	fd_set fdsr;
	int fd_A[server.maxclient];
	for(int m=0; m<server.maxclient; m++){
		fd_A[m]=0;	
	}
	fd_A[0]=conn.sock;
	int flag = 0;
	int passed_burst = 0;
	recv_list.insert(pair<int,int>(0,0));
	anr_list.insert(pair<int,double>(0,0));
	for(int i = 0;i < RAW_TYPE_SERVER;i++){
		recv_list.insert(pair<int,int>(i+1,0));
		anr_list.insert(pair<int,double>(i+1,0));
	}
	map<int, int>::iterator recv_it;
	map<int, int>::iterator tmp_it;
	map<int, double>::iterator anr_it;
	//loop
	while(1){
		server.rset = 0;
		FD_ZERO(&fdsr);
		//FD_SET(server.incoming_sock, &fdsr);
		FD_SET(conn.sock, &fdsr);
		server.tv.tv_sec  = 120;
		server.tv.tv_usec = 0;
		//struct Packet p;
		struct buffer result;
		char* message;
		// push all connection to the fd set
		for(int i=0; i<server.maxclient; i++){
			if(fd_A[i]!=0){
				FD_SET(fd_A[i],&fdsr);
			}	
		}
		// execute select call
		server.rset = select(server.maxfdp+1, &fdsr, NULL, NULL, &server.tv);
		if(server.rset <0){
			perror("select");
			break;
		}else if(server.rset ==0){
			printf("timeout\n");
			continue;	
		}
		// check all sockfd in the fd set
		
		if(FD_ISSET(fd_A[0],&fdsr)){
			server.rset = recv(fd_A[0], sock_message, BUFFER_SIZE,0);
			if(server.rset<=0){
				printf("client close\n");
				close(fd_A[0]);
			}else{
				if(server.rset<BUFFER_SIZE){
					memset(&message,0,sizeof(message));
					int hdr_type = gettype(sock_message);//get header type
					if(hdr_type == LOW_LEVEL_INI){
						//get the initial packet from client and response with ack pakcet
						//get attribute payload
						char * _burst_time = getmessage(sock_message);
						char * _round_time = getnextmessage(sock_message);
						server.burst_time = atoi(_burst_time);
						server.round_time = atoi(_round_time);
						char _serv_raw_port[1];
						sprintf(_serv_raw_port,"%d", serv_raw_port);
						Packet p = build_packet(LOW_LEVEL_ACK, RAW_PORT,_serv_raw_port);
						result = encode(p);
						send(fd_A[0], result.mem, result.size, 0);
						memset(&sock_message[server.rset],'\0',1);
					}else if(hdr_type == BURST_READY){
						//get burst ready packet from client and build rawlistener in server side
						//get attribute payload
						char* _cli_raw_port = getmessage(sock_message);
						int cli_raw_port = atoi(_cli_raw_port);
						struct sockaddr_in serv, cli;
						char serv_ip[256];
						char cli_ip[256];
						socklen_t serv_len = sizeof(serv);
						socklen_t cli_len = sizeof(cli);
						getsockname(conn.sock, (struct sockaddr *)&serv, &serv_len);
						getpeername(conn.sock, (struct sockaddr *)&cli, &cli_len);
						inet_ntop(AF_INET, &serv.sin_addr, serv_ip, sizeof(serv_ip));
						inet_ntop(AF_INET, &cli.sin_addr, cli_ip, sizeof(cli_ip));
						// create raw sockets for TCP UDP AND ICMP
						RawListener _stcp_raw(cli_ip,serv_ip,cli_raw_port,serv_raw_port,IPPROTO_TCP);
						RawListener _sudp_raw(cli_ip,serv_ip,cli_raw_port+1,
								serv_raw_port+1,IPPROTO_UDP);
						RawListener _sicmp_raw(cli_ip,serv_ip,cli_raw_port+2,
								serv_raw_port+2,IPPROTO_ICMP);
						stcp_raw = _stcp_raw;
						sudp_raw = _sudp_raw;
						sicmp_raw = _sicmp_raw;
						if(stcp_raw.sockfd==null||sudp_raw.sockfd==null||sicmp_raw.sockfd==null) {
							printf("Failed to opne RAW CONNECTION!\n");
							conn.close_connection();
							exit(1);//stop program
						}else{
							fd_A[1]=stcp_raw.sockfd;
							fd_A[2]=sudp_raw.sockfd;
							fd_A[3]=sicmp_raw.sockfd;
							for(int k=0; k<server.maxclient; k++){
								if(fd_A[k]>server.maxfdp){
									server.maxfdp = fd_A[k];
								}	
							}
							char* output = "success";
							Packet p = build_packet(BURST_READY,ACK,output);
							result = encode(p);
							server.rset = send(fd_A[0], result.mem, result.size, 0);
							if(server.rset<=0){
								printf("Cannot send your BURST READY message!\n");
								close(fd_A[0]);
								exit(1);
							}
							memset(&sock_message[server.rset],'\0',1);
						}
						
					}
				}
			}
		}else if(FD_ISSET(fd_A[1],&fdsr)){
			server.rset = stcp_raw.recv(sock_message, BUFFER_SIZE);
			if(server.rset<=0){
				printf("tcp raw socket close\n");
				close(fd_A[1]);
			}else{
				int _type = getid(sock_message);
				printf("TCP[%d]\n", _type);
				if(_type != CLI_END_SIGNAL){
					recv_it = recv_list.find(_type);
					(recv_it->second) = (recv_it->second) + 1;
				}else if(_type == CLI_END_SIGNAL){
					//get the signel, calculate the rank, reset the vector;
					for(recv_it=recv_list.begin();recv_it!=recv_list.end();recv_it++){
						printf("packetType[%d],get[%d]\n",recv_it->first,recv_it->second);
					}
					
					//algorithm to calculate the rank
					for(recv_it=recv_list.begin();recv_it!=recv_list.end();recv_it++){
	        				if(recv_it->first != 0){
							int pos = recv_it->first;
							int tmp = recv_it->second;
							int midrank_arg = 0;
							double rank = RAW_TYPE_SERVER;
							for(tmp_it=recv_list.begin();tmp_it!=recv_list.end();tmp_it++){
								if(tmp_it->second >= tmp && tmp_it->first != pos){
									if(tmp_it->second == tmp){
										midrank_arg ++;
									}
									rank = rank -1;
								}
							}
							rank = rank + midrank_arg * 0.5;//normalize to midrank
							printf("1111Type[%d],NORMAL_RANK[%f]\n",pos,rank);
							anr_it = anr_list.find(pos);
							double _nrank = rank/RAW_TYPE_SERVER;
							double _oldnrank = anr_it->second;
							anr_it->second = (_oldnrank*passed_burst+_nrank)/(passed_burst+1);
						}
	    				}
					passed_burst++;
					//midrank order
					//show value
					for(anr_it=anr_list.begin();anr_it!=anr_list.end();anr_it++){
						printf("Type[%d],ANR_RANK[%f]\n",anr_it->first,anr_it->second);
					}
					printf("passed_burst[%d]\n",passed_burst);
					//save statistic anr to file
					if(passed_burst == server.burst_time){
						FILE *fp=fopen("result.txt","w");
						fprintf(fp,"Packet Type @ ANR Rank\n");
						for(anr_it=anr_list.begin();anr_it!=anr_list.end();anr_it++){
							fprintf(fp,"Type[%d],ANR_RANK[%f]\n",anr_it->first,anr_it->second);
						}
						fclose(fp);
					}
				}
				
			}
			memset(&sock_message[server.rset],'\0',1);
		}else if(FD_ISSET(fd_A[2],&fdsr)){
			server.rset = sudp_raw.recv(sock_message, BUFFER_SIZE);
			if(server.rset<=0){
				printf("udp raw socket close\n");
				close(fd_A[2]);
			}else{
				int _type = getid(sock_message);
				printf("UDP[%d]\n", _type);
				if(_type != CLI_END_SIGNAL){
					recv_it = recv_list.find(_type);
					(recv_it->second) = (recv_it->second) + 1;
				}
			}
			memset(&sock_message[server.rset],'\0',1);
		}else if(FD_ISSET(fd_A[3],&fdsr)){
			server.rset = sicmp_raw.recv(sock_message, BUFFER_SIZE);
			if(server.rset<=0){
				printf("icmp raw socket close\n");
				close(fd_A[3]);
			}else{
				int _type = getid(sock_message);
				printf("ICMP[%d]\n", _type);
				if(_type != CLI_END_SIGNAL){
					recv_it = recv_list.find(_type);
					(recv_it->second) = (recv_it->second) + 1;
				}
			}
			memset(&sock_message[server.rset],'\0',1);
		}
	}	
}

void TCPServer::stop_server() {
	close(incoming_sock);
}
